// For future reference, here is what Transports need to do in Mirror:
//
// Connecting:
//   * Transports are responsible to call either OnConnected || OnDisconnected
//     in a certain time after a Connect was called. It can not end in limbo.
//
// Disconnecting:
//   * Connections might disconnect voluntarily by the other end.
//   * Connections might be disconnect involuntarily by the server.
//   * Either way, Transports need to detect it and call OnDisconnected.
//
// Timeouts:
//   * Transports should expose a configurable timeout
//   * Transports are responsible for calling OnDisconnected after a timeout
//
// Channels:
//   * Default channel is Reliable, as in reliable ordered (OR DISCONNECT)
//   * Where possible, Unreliable should be supported (unordered, no guarantee)
//
// Other:
//   * Transports functions are all bound to the main thread.
//     (Transports can use other threads in the background if they manage them)
//   * Transports should only process messages while the component is enabled.
//

using System;
using UnityEngine;

namespace Common.Network
{
    /// <summary>
    /// <para>Abstract transport layer component </para>
    /// 基于 Mirror修改，Mirror包含服务器，这里只需要客户端部分；Mirror包含不可靠消息，这里全可靠
    /// </summary>
    public abstract class Transport : MonoBehaviour
    {
        [Tooltip("服务器地址")] public string networkAddress = "192.168.110.2";

        [Tooltip("服务器端口")] public ushort port = 5001;

        /// <summary>Is this transport available in the current platform? </summary>
        public abstract bool Available();

        /// <summary>Called by Transport when the client connected to the server. </summary>
        public Action OnClientConnected;

        /// <summary>Called by Transport when the client received a message from the server. </summary>
        public Action<ArraySegment<byte>> OnClientDataReceived;

        /// <summary>Called by Transport when the client sent a message to the server. </summary>
        // Transports are responsible for calling it because:
        // - groups it together with OnReceived responsibility
        // - allows transports to decide if anything was sent or not
        // - allows transports to decide the actual used channel (i.e. tcp always sending reliable)
        public Action<ArraySegment<byte>> OnClientDataSent;

        /// <summary>Called by Transport when the client encountered an error. </summary>
        public Action<TransportError, string> OnClientError;

        /// <summary>Called by Transport when the client disconnected from the server. </summary>
        public Action OnClientDisconnected;

        /// <summary>
        /// 发送心跳
        /// </summary>
        public Action SendHeart;


        /// <summary>True if the client is currently connected to the server. </summary>
        public abstract bool ClientConnected();

        /// <summary>Connects the client to the server at the address. </summary>
        public abstract void ClientConnect(string address, ushort port);

        /// <summary>Connects the client to the server at the Uri. @</summary>
        public virtual void ClientConnect(Uri uri, ushort port)
        {
            // By default, to keep backwards compatibility, just connect to the host
            // in the uri
            ClientConnect(uri.Host, port);
        }

        /// <summary>Sends a message to the server over the given channel. @</summary>
        // The ArraySegment is only valid until returning. Copy if needed.
        public abstract void ClientSend(ArraySegment<byte> segment);

        /// <summary>Disconnects the client from the server </summary>
        public abstract void ClientDisconnect();


        /// <summary>Maximum message size for the given channel.</summary>
        // Different channels often have different sizes, ranging from MTU to
        // several megabytes.
        //
        // Needs to return a value at all times, even if the Transport isn't
        // running or available because it's needed for initializations.
        public abstract int GetMaxPacketSize();

        /// <summary>Recommended Batching threshold for this transport. </summary>
        // Uses GetMaxPacketSize by default.
        // Some transports like kcp support large max packet sizes which should
        // not be used for batching all the time because they end up being too
        // slow (head of line blocking etc.).
        public virtual int GetBatchThreshold()
        {
            return GetMaxPacketSize();
        }

        // block Update & LateUpdate to show warnings if Transports still use
        // them instead of using
        //   Client/ServerEarlyUpdate: to process incoming messages
        //   Client/ServerLateUpdate: to process outgoing messages
        // those are called by NetworkClient/Server at the right time.
        //
        // allows transports to implement the proper network update order of:
        //      process_incoming()
        //      update_world()
        //      process_outgoing()
        //
        // => see NetworkLoop.cs for detailed explanations!
#pragma warning disable UNT0001 // Empty Unity message
        public void Update()
        {
        }

        public void LateUpdate()
        {
        }
#pragma warning restore UNT0001 // Empty Unity message

        /// <summary>
        /// NetworkLoop NetworkEarly/LateUpdate were added for a proper network
        /// update order. the goal is to:
        ///    process_incoming()
        ///    update_world()
        ///    process_outgoing()
        /// in order to avoid unnecessary latency and data races.
        /// </summary>
        // => split into client and server parts so that we can cleanly call
        //    them from NetworkClient/Server
        // => VIRTUAL for now so we can take our time to convert transports
        //    without breaking anything.
        public virtual void ClientEarlyUpdate()
        {
        }

        public virtual void ClientLateUpdate()
        {
        }

        /// <summary>Shut down the transport</summary>
        public abstract void Shutdown();

        /// <summary>Called by Unity when quitting. Inheriting Transports should call base for proper Shutdown.</summary>
        public virtual void OnApplicationQuit()
        {
            // stop transport (e.g. to shut down threads)
            // (when pressing Stop in the Editor, Unity keeps threads alive
            //  until we press Start again. so if Transports use threads, we
            //  really want them to end now and not after next start)
            Shutdown();
        }
    }
}